{
 "cells": [
  {
   "metadata": {},
   "cell_type": "markdown",
   "source": "从这里可以看出使用pydantic进行schema描述和output解释的原理（不用tool）",
   "id": "6137edd001a6ec90"
  },
  {
   "cell_type": "code",
   "id": "initial_id",
   "metadata": {
    "collapsed": true,
    "ExecuteTime": {
     "end_time": "2025-08-01T06:04:31.604898Z",
     "start_time": "2025-08-01T06:04:30.617196Z"
    }
   },
   "source": [
    "from typing import List\n",
    "\n",
    "from langchain_core.output_parsers import PydanticOutputParser\n",
    "from langchain_core.prompts import ChatPromptTemplate\n",
    "from pydantic import BaseModel, Field\n",
    "\n",
    "\n",
    "class Person(BaseModel):\n",
    "    \"\"\"Information about a person.\"\"\"\n",
    "\n",
    "    name: str = Field(..., description=\"The name of the person\")\n",
    "    height_in_meters: float = Field(\n",
    "        ..., description=\"The height of the person expressed in meters.\"\n",
    "    )\n",
    "\n",
    "\n",
    "class People(BaseModel):\n",
    "    \"\"\"Identifying information about all people in a text.\"\"\"\n",
    "\n",
    "    people: List[Person]\n",
    "\n",
    "\n",
    "# Set up a parser\n",
    "parser = PydanticOutputParser(pydantic_object=People)\n",
    "\n",
    "# Prompt\n",
    "prompt = ChatPromptTemplate.from_messages(\n",
    "    [\n",
    "        (\n",
    "            \"system\",\n",
    "            \"Answer the user query. Wrap the output in `json` tags\\n{format_instructions}\",\n",
    "        ),\n",
    "        (\"human\", \"{query}\"),\n",
    "    ]\n",
    ").partial(format_instructions=parser.get_format_instructions())"
   ],
   "outputs": [],
   "execution_count": 1
  },
  {
   "metadata": {
    "ExecuteTime": {
     "end_time": "2025-08-01T06:04:34.086869Z",
     "start_time": "2025-08-01T06:04:31.611170Z"
    }
   },
   "cell_type": "code",
   "source": [
    "query = \"Anna is 23 years old and she is 6 feet tall\"\n",
    "\n",
    "from init_llm_model import *\n",
    "print(prompt.invoke({\"query\": query}).to_string())"
   ],
   "id": "8a2d63eb93c82e78",
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "System: Answer the user query. Wrap the output in `json` tags\n",
      "The output should be formatted as a JSON instance that conforms to the JSON schema below.\n",
      "\n",
      "As an example, for the schema {\"properties\": {\"foo\": {\"title\": \"Foo\", \"description\": \"a list of strings\", \"type\": \"array\", \"items\": {\"type\": \"string\"}}}, \"required\": [\"foo\"]}\n",
      "the object {\"foo\": [\"bar\", \"baz\"]} is a well-formatted instance of the schema. The object {\"properties\": {\"foo\": [\"bar\", \"baz\"]}} is not well-formatted.\n",
      "\n",
      "Here is the output schema:\n",
      "```\n",
      "{\"$defs\": {\"Person\": {\"description\": \"Information about a person.\", \"properties\": {\"name\": {\"description\": \"The name of the person\", \"title\": \"Name\", \"type\": \"string\"}, \"height_in_meters\": {\"description\": \"The height of the person expressed in meters.\", \"title\": \"Height In Meters\", \"type\": \"number\"}}, \"required\": [\"name\", \"height_in_meters\"], \"title\": \"Person\", \"type\": \"object\"}}, \"description\": \"Identifying information about all people in a text.\", \"properties\": {\"people\": {\"items\": {\"$ref\": \"#/$defs/Person\"}, \"title\": \"People\", \"type\": \"array\"}}, \"required\": [\"people\"]}\n",
      "```\n",
      "Human: Anna is 23 years old and she is 6 feet tall\n"
     ]
    }
   ],
   "execution_count": 2
  },
  {
   "metadata": {
    "ExecuteTime": {
     "end_time": "2025-08-01T06:06:42.599457Z",
     "start_time": "2025-08-01T06:06:32.418514Z"
    }
   },
   "cell_type": "code",
   "source": "llm.invoke(prompt.invoke({\"query\": query}))",
   "id": "6d0b8a44dce58e9d",
   "outputs": [
    {
     "data": {
      "text/plain": [
       "AIMessage(content='```json\\n{\\n  \"people\": [\\n    {\\n      \"name\": \"Anna\",\\n      \"height_in_meters\": 1.8288\\n    }\\n  ]\\n}\\n```', additional_kwargs={'refusal': None}, response_metadata={'token_usage': {'completion_tokens': 37, 'prompt_tokens': 310, 'total_tokens': 347, 'completion_tokens_details': None, 'prompt_tokens_details': {'audio_tokens': None, 'cached_tokens': 0}, 'prompt_cache_hit_tokens': 0, 'prompt_cache_miss_tokens': 310}, 'model_name': 'deepseek-chat', 'system_fingerprint': 'fp_8802369eaa_prod0623_fp8_kvcache', 'id': '04bb48c7-bbdd-490c-a91b-0fefb1a26f39', 'service_tier': None, 'finish_reason': 'stop', 'logprobs': None}, id='run--9c86db7a-301a-43bd-bbf0-43ae5d5eeaed-0', usage_metadata={'input_tokens': 310, 'output_tokens': 37, 'total_tokens': 347, 'input_token_details': {'cache_read': 0}, 'output_token_details': {}})"
      ]
     },
     "execution_count": 4,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "execution_count": 4
  },
  {
   "metadata": {
    "ExecuteTime": {
     "end_time": "2025-08-01T06:07:24.392757Z",
     "start_time": "2025-08-01T06:07:18.560537Z"
    }
   },
   "cell_type": "code",
   "source": [
    "chain = prompt | llm | parser\n",
    "\n",
    "chain.invoke({\"query\": query})"
   ],
   "id": "cf3253f8f56c00e0",
   "outputs": [
    {
     "data": {
      "text/plain": [
       "People(people=[Person(name='Anna', height_in_meters=1.8288)])"
      ]
     },
     "execution_count": 5,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "execution_count": 5
  },
  {
   "metadata": {},
   "cell_type": "markdown",
   "source": "如果自己做结果解析成 json的实现方式",
   "id": "68aea48205f3387"
  },
  {
   "metadata": {},
   "cell_type": "code",
   "outputs": [],
   "execution_count": null,
   "source": [
    "import json\n",
    "import re\n",
    "from typing import List\n",
    "\n",
    "from langchain_core.messages import AIMessage\n",
    "from langchain_core.prompts import ChatPromptTemplate\n",
    "from pydantic import BaseModel, Field\n",
    "\n",
    "\n",
    "class Person(BaseModel):\n",
    "    \"\"\"Information about a person.\"\"\"\n",
    "\n",
    "    name: str = Field(..., description=\"The name of the person\")\n",
    "    height_in_meters: float = Field(\n",
    "        ..., description=\"The height of the person expressed in meters.\"\n",
    "    )\n",
    "\n",
    "\n",
    "class People(BaseModel):\n",
    "    \"\"\"Identifying information about all people in a text.\"\"\"\n",
    "\n",
    "    people: List[Person]\n",
    "\n",
    "\n",
    "# Prompt\n",
    "prompt = ChatPromptTemplate.from_messages(\n",
    "    [\n",
    "        (\n",
    "            \"system\",\n",
    "            \"Answer the user query. Output your answer as JSON that  \"\n",
    "            \"matches the given schema: \\`\\`\\`json\\n{schema}\\n\\`\\`\\`. \"\n",
    "            \"Make sure to wrap the answer in \\`\\`\\`json and \\`\\`\\` tags\",\n",
    "        ),\n",
    "        (\"human\", \"{query}\"),\n",
    "    ]\n",
    ").partial(schema=People.schema())\n",
    "\n",
    "\n",
    "# Custom parser\n",
    "def extract_json(message: AIMessage) -> List[dict]:\n",
    "    \"\"\"Extracts JSON content from a string where JSON is embedded between \\`\\`\\`json and \\`\\`\\` tags.\n",
    "\n",
    "    Parameters:\n",
    "        text (str): The text containing the JSON content.\n",
    "\n",
    "    Returns:\n",
    "        list: A list of extracted JSON strings.\n",
    "    \"\"\"\n",
    "    text = message.content\n",
    "    # Define the regular expression pattern to match JSON blocks\n",
    "    pattern = r\"\\`\\`\\`json(.*?)\\`\\`\\`\"\n",
    "\n",
    "    # Find all non-overlapping matches of the pattern in the string\n",
    "    matches = re.findall(pattern, text, re.DOTALL)\n",
    "\n",
    "    # Return the list of matched JSON strings, stripping any leading or trailing whitespace\n",
    "    try:\n",
    "        return [json.loads(match.strip()) for match in matches]\n",
    "    except Exception:\n",
    "        raise ValueError(f\"Failed to parse: {message}\")"
   ],
   "id": "2a3a01df95caa2b6"
  },
  {
   "metadata": {},
   "cell_type": "code",
   "outputs": [],
   "execution_count": null,
   "source": "",
   "id": "fa737586ad37a082"
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 2
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython2",
   "version": "2.7.6"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
